﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using PokeData;
using System.Data;

namespace PokeTD
{
    public class Tower
    {
        public event ClickedEventArgsHandler Clicked;

        private static Texture2D towerBase;

        private int _TowerType;
        private Texture2D towerTexture;
        private Texture2D bulletTexture;
        private string _name;
        private Vector2 _position;
        private int _cost;

        private int _attackMin;
        private int _attackMax;
        private int _attackSpeed;
        private bool _spAttack;
        private bool _canAttackAir;
        private bool _canAttackGround;
        private int _UpgradeTo;
        private int _type1;
        private int _type2;

        private string _effectName;
        private int _effectProbability;
        private float _effectDuration;

        private double _range;

        private Creep _target;
        private List<Bullet> bullets = new List<Bullet>();
        private MouseState lastState = Mouse.GetState();

        private float lastTimeAttacked;

        public static Texture2D TowerBase {
            get { return Tower.towerBase; }
            set { Tower.towerBase = value; }
        }

        public int TowerType {
            get { return _TowerType; }
            set { _TowerType = value; }
        }
        public string Name {
            get { return _name; }
            set { _name = value; }
        }
        public Vector2 Position {
            get { return _position; }
            set { _position = value; }
        }
        public Vector2 Center {
            get { return _position * 32 + new Vector2(Block.BlockWidth/2, Block.BlockHeight/2); }
        }
        public int Cost {
            get { return _cost; }
            set { _cost = value; }
        }
        public int AttackMin {
            get { return _attackMin; }
            set { _attackMin = value; }
        }
        public int AttackMax {
            get { return _attackMax; }
            set { _attackMax = value; }
        }
        public int AttackSpeed {
            get { return _attackSpeed; }
            set { _attackSpeed = value; }
        }
        public bool SpAttack {
            get { return _spAttack; }
            set { _spAttack = value; }
        }
        public bool canAttackAir
        {
            get { return _canAttackAir; }
            set { _canAttackAir = value; }
        }
        public bool canAttackGround
        {
            get { return _canAttackGround; }
            set { _canAttackGround = value; }
        }
        public int UpgradeTo
        {
            get { return _UpgradeTo; }
            set { _UpgradeTo = value; }
        }
        public int Type1 {
            get { return _type1; }
            set { _type1 = value; }
        }
        public int Type2 {
            get { return _type2; }
            set { _type2 = value; }
        }

        public double Range {
            get { return _range; }
            set { _range = value; }
        }

        public string EffectName
        {
            get { return _effectName; }
            set { _effectName = value; }
        }

        public int EffectProbability
        {
            get { return _effectProbability; }
            set { _effectProbability = value; }
        }

        public float EffectDuration
        {
            get { return _effectDuration; }
            set { _effectDuration = value; }
        }

        public Creep Target
        {
            get { return _target; }
            set { _target = value; }
        }

        List<TowerEffect> TowerEffects = new List<TowerEffect>();


        /// <summary>
        /// Creep angreifen
        /// </summary>
        /// <param name="Target">Ziel</param>
        public void Update(GameTime gameTime, List<Creep> creeps, Vector2 offset) {
            lastTimeAttacked += (float)gameTime.ElapsedGameTime.TotalSeconds;
            if (!isTargetInRange() || _target.isDead) {
                _target = null;
            }
            if (_target != null && lastTimeAttacked >  1/(float)AttackSpeed) {
                Vector2 direction = (_target.Center) - (Center);
                direction.Normalize();
                Bullet bullet = new Bullet(bulletTexture, direction);
                bullet.Position = Center - new Vector2(bulletTexture.Width, bulletTexture.Height) / 2;
                bullets.Add(bullet);

                lastTimeAttacked = 0;
            }
            for (int i = 0; i < bullets.Count; i++) {
                bullets[i].Update(gameTime);
                if (!isInRange(bullets[i].Center)) {
                    bullets[i].isOutOfRange = true;
                }
                if (_target != null && creepHit(bullets[i], creeps)) {
                    Random dmgrnd = new Random();
                    if (_attackMin > _attackMax)
                    {
                        int d = _attackMin;
                        _attackMin = _attackMax;
                        _attackMax = d;
                    }
                    int dmg = dmgrnd.Next(_attackMin, _attackMax);
                    _target.HP = (int)(_target.HP - (double)dmg * Globals.checkEff((Typ)_type1, _target.Typ1, _target.Typ2));
                    bullets[i].isOutOfRange = true;

                    if (!_target.isDead && dmgrnd.Next(0, 100) < EffectProbability)
                    {
                        TowerEffect effect = TowerEffect.Get(EffectName, _target, EffectDuration);
                        if (effect != null)
                            TowerEffects.Add(effect);
                    }
                }
                if (bullets[i].isOutOfRange) {
                    bullets.Remove(bullets[i]);
                }
            }
            MouseState state = Mouse.GetState();

            for (int i = 0; i < TowerEffects.Count; i++)
            {
                TowerEffects[i].Update(gameTime);
                if (!TowerEffects[i].Alive)
                    TowerEffects.RemoveAt(i--);
            }

            if (lastState.LeftButton == ButtonState.Released && state.LeftButton == ButtonState.Pressed && (new Rectangle((int)offset.X + (int)_position.X * Block.BlockWidth, (int)offset.Y + (int)_position.Y * Block.BlockHeight, Block.BlockWidth, Block.BlockHeight).Contains(new Point(state.X, state.Y))))
            {
                //Click'ed.
                Clicked(this, new ClickedEventArgs());
            }

            lastState = Mouse.GetState();
        }

        public void Draw(SpriteBatch spriteBatch, Vector2 renderRoot) {
            spriteBatch.Draw(towerBase, Position * new Vector2(Block.BlockWidth, Block.BlockHeight) + renderRoot, Color.White);
            Vector2 direction;
            if (this.Target == null) {
                direction = new Vector2(0, -1);
            } else {
                direction = this.Center - this.Target.Center;
                direction.Normalize();
            }
            int viewDirection = (int)Math.Round(Math.Atan2(-direction.X, direction.Y) / (Math.PI / 2));
            switch (viewDirection) {
                        case 0:
                            viewDirection = 0;
                            break;
                        case -1:
                            viewDirection = 32;
                            break;
                        case 2:
                            viewDirection = 64;
                            break;
                        case -2:
                            viewDirection = 64;
                            break;
                        case 1:
                            viewDirection = 96;
                            break;
                    }
            Vector2 pos = Position * new Vector2(Block.BlockWidth, Block.BlockHeight) + renderRoot;
            spriteBatch.Draw(towerTexture, pos, new Rectangle(viewDirection, 0, 32, 32), Color.White);
            foreach (Bullet bullet in bullets) {
                spriteBatch.Draw(bulletTexture, bullet.Position + renderRoot, Color.White);
            }
            foreach (TowerEffect effect in TowerEffects)
            {
                effect.Draw(spriteBatch, renderRoot);
            }
        }

        public Tower()
        {
        }

        /// <summary>
        /// Erstellt einen neuen Tower aufgrund der in der Datenbank angegebenen Werten.
        /// </summary>
        /// <param name="TID">ID des Towers in der Datenbank</param>
        /// <returns>Neue Towerinstanz</returns>
        public Tower(int TID, Texture2D towerTexture, Texture2D bulletTexture) {
            var a = from s in Globals.waveDS.Towers
                    where s.id == TID
                    select s;

            WavesDS.TowersRow r = a.First<WavesDS.TowersRow>();
            TowerType = r.id;   
            Name = r.Name;
            Cost = r.Cost;
            AttackMin = r.AttackMin;
            AttackMax = r.AttackMax;
            AttackSpeed = r.AttackSpeed;
            SpAttack = r.SpAttack;
            canAttackAir = r.CanAttackAir;
            canAttackGround = r.CanAttackGround;
            UpgradeTo = r.UpgradeTo;
            Type1 = r.Type1;
            Type2 = r.Type2;
            Range = r.Range;
            EffectName = r.EffectName;
            EffectProbability = r.EffectProbability;
            EffectDuration = (float)r.EffectDuration / 1000f;

            this.towerTexture = towerTexture;
            this.bulletTexture = bulletTexture;

            lastTimeAttacked = _attackSpeed;

        }

        public Tower(int TowerType, Texture2D towerTexture, Texture2D bulletTexture, string name, int cost, int Amin, int Amax, int Aspeed, bool SpAtt, bool canAttAir, int UpTo, int type1, int type2, double range) {
            _TowerType = TowerType;
            _name = name;
            _cost = cost;
            _attackMin = Amin;
            _attackMax = Amax;
            _attackSpeed = Aspeed;
            _spAttack = SpAtt;
            _canAttackAir = canAttAir;
            _UpgradeTo = UpTo;
            _type1 = type1;
            _type2 = type2;
            _range = range;

            this.towerTexture = towerTexture;
            this.bulletTexture = bulletTexture;

            lastTimeAttacked = _attackSpeed;
        }

        public bool isTargetInRange()
        {
            if (Target == null) {
                return false;
            } else {
                return isInRange(Target.Center);
            }
        }

        public bool isInRange(Vector2 position) {
            return (Vector2.Distance(Center, position) <= _range);
        }

        public void ChooseTarget(List<Creep> creeps) {
            _target = null;
            double smallestRange = _range;

            foreach (Creep creep in creeps) {
                if (!creep.isDead && creep.isSpawned && Vector2.Distance(Center, creep.Center) < smallestRange) {
                    if ((!canAttackAir && creep.isFlying) || (!canAttackGround && !creep.isFlying)) continue;
                    smallestRange = Vector2.Distance(Center, creep.Center);
                    _target = creep;
                }
            }
        }

        private bool creepHit(Bullet bullet, List<Creep> creeps) {
            foreach (Creep creep in creeps) {
                int range;
                if (Vector2.Distance(Center, creep.Center)/32 < 2) {
                    range = 8;
                } else {
                    range = 16;
                }
                if (!creep.isDead && creep.isSpawned && Vector2.Distance(bullet.Center, creep.Center) < range) {
                    _target = creep;
                    return true;
                }
            }
            return false;
        }
    }
}
